#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include "common.h"
// #include "HBA_shm.h"

#define HBA_DRIVER_NAME "tp_hosts"
const char *HBA_SCSI_VENDOR = "TrustedPath";
const char *HBA_SCSI_MODEL = "TP";
static struct Scsi_Host *VHOST = NULL;
static struct scsi_device *PHY_DEV = NULL;
static struct scsi_device *VLUN_DEV = NULL;

static int HBA_SlaveAlloc(struct scsi_device *sdev);
static int HBA_QueueCommand(struct Scsi_Host *shost, struct scsi_cmnd *cmd);

// Scsi_host的父设备
static struct device g_hbaDevice = {
	.init_name 	= HBA_DRIVER_NAME,
};

static struct scsi_host_template g_hbaTemplate = {
	.name = "tp_sht",
	.proc_name = "trusted_path_proc",
	.module = THIS_MODULE,
	.this_id = -1,
	.can_queue = 4096,
	.max_sectors = 0xFFFF,
	.cmd_per_lun = 32,
	.sg_tablesize = 4096,
	.dma_boundary = PAGE_SIZE - 1,
	.track_queue_depth = 1,
	.slave_alloc = HBA_SlaveAlloc,
	.queuecommand = HBA_QueueCommand,
};

static int rq_bio_constructor(struct bio *bio, struct bio *bio_orig, void *data)
{
	bio->bi_end_io = bio_orig->bi_end_io;
	return 0;
}

enum rq_end_io_ret my_rq_end_io_fn(struct request *rq, blk_status_t status)
{
	struct request *rq_src;
	struct scsi_cmnd *cmd;
	rq_src = rq->end_io_data;
	cmd = blk_mq_rq_to_pdu(rq_src);
	cmd->result = DID_OK << 16;
	blk_mq_free_request(rq);
	scsi_done(cmd);
	TP_LOG_INFO("scsi_done");
	return RQ_END_IO_FREE;
}

static int HBA_QueueCommand(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
{
	int ret = 0;
	struct request *rq;
	struct request *rq_src;
	struct request_queue *q;

	TP_LOG_INFO("HBA_QueueCommand");

	rq_src = blk_mq_rq_from_pdu(cmd);
	q = PHY_DEV->request_queue;
	rq = blk_mq_alloc_request(q, rq_src->cmd_flags, BLK_MQ_REQ_NOWAIT);
	if (rq == NULL) {
		return 1;
	}
	ret = blk_rq_prep_clone(rq, rq_src, rq_src->bio->bi_pool, GFP_ATOMIC, rq_bio_constructor, NULL);
	if (ret) {
		return ret;
	}
	rq->end_io = my_rq_end_io_fn;
	rq->end_io_data = rq_src;
	blk_execute_rq_nowait(rq, true);
	return 0;
}

static int HBA_SlaveAlloc(struct scsi_device *sdev)
{
	TP_LOG_INFO("HBA_SlaveAlloc");
	sdev->vendor = HBA_SCSI_VENDOR;
	sdev->model = HBA_SCSI_MODEL;
	return 0;
}

static void HBA_CopyHostTempAttr(const struct scsi_host_template *srcHostt, struct scsi_host_template *dstHostt)
{
	dstHostt->can_queue = srcHostt->can_queue;
	dstHostt->this_id = srcHostt->this_id;
	dstHostt->sg_prot_tablesize = srcHostt->sg_prot_tablesize;
	dstHostt->max_sectors = srcHostt->max_sectors;
	dstHostt->dma_boundary = srcHostt->dma_boundary;
	dstHostt->cmd_per_lun = srcHostt->cmd_per_lun;
	dstHostt->cmd_size = srcHostt->cmd_size;
	dstHostt->emulated = 1;
	dstHostt->supported_mode = srcHostt->supported_mode;
}

static void HBA_RegisterHost(const struct Scsi_Host *phyHost)
{
	int ret;
	HBA_CopyHostTempAttr(phyHost->hostt, &g_hbaTemplate);
	VHOST = scsi_host_alloc(&g_hbaTemplate, sizeof(struct Scsi_Host));
	if (VHOST == NULL) {
		TP_LOG_INFO("Failed to alloc scsi host.");
	}
	ret = scsi_add_host(VHOST, NULL);
	if (ret != 0) {
		TP_LOG_INFO("Failed to add scsi host.");
	}
}

static void HBA_UnRegisterHost(struct Scsi_Host *VHOST)
{
	// RemoveHost(VHOST);
	scsi_remove_host(VHOST);
	scsi_host_put(VHOST);
}

// 根据物理设备创建虚拟设备
struct scsi_device *HBA_CreateVlun(struct scsi_device *phyDev, uint channel, uint target, u64 lun)
{
	HBA_RegisterHost(phyDev->host);
	if (VHOST != NULL) {
		VLUN_DEV = __scsi_add_device(VHOST, phyDev->channel, phyDev->id, phyDev->lun, NULL);
		PHY_DEV = phyDev;
		return VLUN_DEV;
	}
	return NULL;
}

int HBA_RemoveVlun(struct scsi_device *vlunDev)
{
	HBA_UnRegisterHost(VHOST);
	scsi_remove_device(vlunDev);
	return 0;
}

// 注册Scsi_host的父设备
static int HBA_RegisterDevice(void)
{
	int ret;
    // ret = device_register(&g_hbaDevice);
    // if (ret) {
    //     TP_LOG_INFO("Failed to register HBA device.");
	// 	return TP_ERROR;
    // }
	TP_LOG_INFO("Successfully registered HBA device.");
	return TP_OK;
}

static void HBA_UnRegisterDevice(void)
{
    // device_unregister(&g_hbaDevice);
}

int HBA_DriverInit(void)
{
	HBA_RegisterDevice();
	return 0;
}

void HBA_DriverExit(void)
{
	HBA_UnRegisterDevice();
}

